/*
** Example name : gol_serial_socket.c
**
** Network Socket is added...
**
** golc_serial	: Client
**
** Version	: 1.37
**
** Updated by 2011. 09. 23
**
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>

#define NI		(20)		// array x size
#define NJ		(20)		// array y size
//int	NI;				// array x size
//int	NJ;				// array y size

//#define	_DEBUG		1

/*
#define NSTEPS		(100)		// number of time steps
*/

int	nSteps = 100;
int	nSleepSec = 1;

#define	PORT		(20111)		// tcp/ip socket port
#define	BUF_SIZE	10240

// function prototype
void	error_handling	(char *message);
void	showGridState	(int **buf);
void	alloc2dArray	(int ***array, int nRow, int nColumn);
void	free2dArray	(int ***array, int n);
void	getRandomValue	(int **gol_buf);
void	calEvolution	(int **gol_buf);
int	getNeighborCount(int i, int j, int **tmp);
int	getGridX	(void);
int	getGridY	(void);
void	setNSteps	(int steps);
int 	getNSteps	(void);
int	getSleepSec	(void);
void	setSleepSec	(int sec);
void	writeIntMsg	(int sc, char *key, int value);
void	writeStrMsg	(int sc, char *key, char *value);
void 	strSplit	(char *str, char *delim, char buf[2][100]);

//
// start of main
//
int main(int argc, char *argv[])
{
	int	serv_sock;
	//int	clnt_addr_size;
	struct	sockaddr_in	serv_addr;
	char	sMsg[BUF_SIZE] = "";
	int	i, j, k, isum;
	int	**gol_buf;	//, **tmp_buf;
	int	nReadAmount;
	char	sReadBuf[100];
	char	sSleepBuf[2][100];

#if _DEBUG
//	int	a = 0, b = 0;
#endif

	if (argc != 3) {
		printf("> Usage:\n");
		printf(">       ./gol_serial [seconds] [steps]\n");
		exit(0);
	}

	alloc2dArray(&gol_buf, getGridX(), getGridY());

	getRandomValue(gol_buf);
	//showGridState(gol_buf);

	setSleepSec(atoi(argv[1]));		// set sleep sec
	setNSteps(atoi(argv[2]));		// set nsteps

	if ((serv_sock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
		error_handling("msg> socket() creation error.");
	else
		printf("msg> socket() successfully created.\n");

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;

	//	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	//	serv_addr.sin_addr.s_addr = inet_addr("210.119.45.105");// cannot connect
	serv_addr.sin_addr.s_addr = inet_addr("192.168.11.24");		// s0003
	serv_addr.sin_port = htons(PORT);

	if (connect(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
		error_handling("msg> connect() error.");
	else
		printf("msg> connect() successfully doned.\n");

	writeIntMsg(serv_sock, "sleep_sec", getSleepSec());
	printf("write> sleep_sec = %d\n", getSleepSec());

	writeIntMsg(serv_sock, "nsteps", getNSteps());
	printf("write> nsteps = %d\n", getNSteps());

	writeIntMsg(serv_sock, "grid_x", getGridX());
	printf("write> grid_x = %d\n", getGridX());

	writeIntMsg(serv_sock, "grid_y", getGridY());
	printf("write> grid_y = %d\n", getGridY());

#if _DEBUG
	printf("debug> NSTEPS : %d\n", getNSteps());
	printf("debug> NI : %d\n", getGridX());
	printf("debug> NJ : %d\n\n", getGridY());
	printf("debug> Wait a moment to finish the calculation...\n");
#endif


#ifdef _DEBUG
	printf("before> nSteps = %d, nSleepSec = %d\n", nSteps, nSleepSec);
#endif

	// Main Loop : time steps
	for (i=0; i<getNSteps(); i++) {

		calEvolution(gol_buf);
		//showGridState();

		for (j=0; j<getGridX(); j++) {
			for (k=0; k<getGridY(); k++) {
				sMsg[j*getGridX()+k] = gol_buf[j][k] + 48;	// convert int to ascii character
			}
		}
#if _DEBUG
	printf("msg> sMsg = %s, msg_len = %d\n", sMsg, (int)strlen(sMsg));
#endif

		writeStrMsg(serv_sock, "grid_data", sMsg);
		printf("write> grid_data = %s\n\n", sMsg);

		printf("msg> Nstep (%4d / %d) : Wait a %d seconds...\n\n", i+1, getNSteps(), getSleepSec());

		if ((nReadAmount = read(serv_sock, sReadBuf, 100)) > 0) {
			printf("read> sReadBuf = %s\n", sReadBuf);
			//printf("read> nReadAmount = %d\n", nReadAmount);
		}

		strSplit(sReadBuf, " ", sSleepBuf);
		setSleepSec(atoi(sSleepBuf[1]));
#ifdef _DEBUG
		printf("msg> setSleepSec(%d) = %d\n", getSleepSec(), getSleepSec());
#endif
		// Iterations are done; sum the number of live cells
		for (j=0, isum=0; j<getGridX(); j++)
			for (k=0; k<getGridY(); k++)
				isum += gol_buf[j][k];

		writeIntMsg(serv_sock, "live_cells", isum);
		printf("write> live_cells = %d\n", isum);
		printf("msg> Number of live cells = %d / %d\n", isum, getGridX()*getGridY());

		printf("msg> setSleepSec(%d) = %d\n", getSleepSec(), getSleepSec());
		sleep(getSleepSec());
	} // end of NStep

	// Iterations are done; sum the number of live cells
	for (i=0, isum=0; i<getGridX(); i++) {
		for (j=0; j<getGridX(); j++)
			isum = isum + gol_buf[i][j];
	}

	printf("msg> Number of live cells = %d\n\n", isum);

	// close ServerSocket
	close(serv_sock);

	// free Memory
	free2dArray(&gol_buf, getGridX());

	return 0;
}

void error_handling(char *message)
{
	printf("debug> %s\n\n", message);
	exit(1);
}

void showGridState(int **buf)
{
	int i, j;


	printf("\n   ");
	for (i=0; i<getGridX(); i++) {
		printf("%2d ", i);
	}
	printf("\n--------------------------------------------------------------------\n");
	for (i=0; i<getGridX(); i++) {
		printf("%2d|", i);
		for (j=0; j<getGridY(); j++) {
			printf("%2d ", buf[i][j]);
		}
		printf("\n");
	}
	//printf("\n--------------------------------------------------------------------\n");
}

void alloc2dArray(int ***array, int nRow, int nColumn)
{
	int	i;

	if ((*array = (int **)malloc(nRow*sizeof(int *))) == NULL)
		error_handling("allocArray() error");

	for (i=0; i<nRow; i++) {
		if (((*array)[i] = (int *)malloc(nColumn*sizeof(int))) == NULL)
			error_handling("allocArray() error");
	}
}

void free2dArray(int ***array, int n)
{
	int	i;

	for (i=0; i<n; i++) free((*array)[i]);
	free(*array);
}

void getRandomValue(int **gol_buf)
{
	int	i, j;

	srand(time(NULL));

	for (i=0; i<getGridX(); i++) {
		for (j=0; j<getGridY(); j++) {
			gol_buf[i][j] = rand() % 2;
		}
	}
}

void calEvolution(int **gol_buf)
{
	int	i, j;
	int	**tmp_buf;
	int	nNeighbors;

	alloc2dArray(&tmp_buf, getGridX(), getGridY());

	// backup gol_buf
	for (i=0; i<getGridX(); i++)
		for (j=0; j<getGridY(); j++)
			tmp_buf[i][j] = gol_buf[i][j];

	for (i=0; i<getGridX(); i++) {
		for(j=0; j<getGridY(); j++) {
			nNeighbors = getNeighborCount(j, i, tmp_buf);

			switch (nNeighbors) {
				case 3:	gol_buf[i][j] = 1;		// -> alive
					break;
				case 2: gol_buf[i][j] = gol_buf[i][j];	// -> no change
					break;
				default:gol_buf[i][j] = 0;		// -> dead
			}
		}
	}

	//for (i=0; i<getGridX(); i++) free(tmp_buf[i]);
	//free(tmp_buf);
	free2dArray(&tmp_buf, getGridX());
}

int getNeighborCount(int i, int j, int **tmp)
{
	int	a, b, c, d, e, f, x, count = 0;

	a = j - 1;
	b = j;
	c = j + 1;
	d = i - 1;
	e = i;
	f = i + 1;

	for (x=0; x<8; x++) {
		switch (x) {
			case 0:
				if (a >= 0 && d >= 0)	count += tmp[a][d];
				break;
			case 1:
				if (a >= 0)		count += tmp[a][e];
				break;
			case 2:
				if (a >= 0 && f < getGridY())	count += tmp[a][f];
				break;
			case 3:
				if (f < getGridY())	count += tmp[b][f];
				break;
			case 4:
				if (c < getGridX() && f < getGridY())	count += tmp[c][f];
				break;
			case 5:
				if (c < getGridX())	count += tmp[c][e];
				break;
			case 6:
				if (c < getGridX() && d >= 0)	count += tmp[c][d];
				break;
			case 7:
				if (d >= 0)		count += tmp[b][d];
				break;
		}
	}

	return count;
}

int getGridX(void)
{
	return	(int)NI;
}

int getGridY(void)
{
	return (int)NJ;
}

void setNSteps(int steps)
{
	nSteps = steps;
}

int getNSteps(void)
{
	return	nSteps;
}

int getSleepSec(void)
{
	return	nSleepSec;
}

void setSleepSec(int sec)
{
	nSleepSec = sec;
}

void writeIntMsg(int sc, char *key, int value)
{
	char	sMsg[BUF_SIZE];

	sprintf(sMsg, "%s %d\n", key, value);

#if _DEBUG
	//printf("sMsg = %s (strlen(sMsg) = %d)\n", sMsg, (int)strlen(sMsg));
#endif
	write(sc, sMsg, strlen(sMsg));
}

void writeStrMsg(int sc, char *key, char *value)
{
	char	sMsg[BUF_SIZE];

	sprintf(sMsg, "%s %s\n", key, value);

#if _DEBUG
	//printf("sMsg = %s (strlen(sMsg) = %d)\n", sMsg, (int)strlen(sMsg));
#endif
	write(sc, sMsg, strlen(sMsg));
}

void strSplit(char *str, char *delim, char buf[2][100])
{
	char	*p;
	int	i = 0;

	p = strtok(str, delim);
	strcpy(buf[i++], p);
#if _DEBUG
	printf("msg> p = %s\n", p);
#endif

	while (p) {
		p = strtok(NULL, delim);

		if (p) {
#if _DEBUG
			printf("msg> p = %s\n", p);
#endif
			strcpy(buf[i++], p);
		}
	}
}
